using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Text;

using usm.SolDev.Common.Config;
using System.Xml.Serialization;

namespace usm.SolDev.Common
{
    [Serializable]
    public class SQLConnectionString : Configuration
    {
        #region Private data
        private string hostName = Constants.DataBase.defaultHostName;
        private int portNumber = Constants.DataBase.defaultSQLPort;
        private string userName = Constants.DataBase.defaultUserName;
        private string password = Constants.DataBase.defaultPassword;
        private string databaseName = Constants.DataBase.defaultDatabaseName;
        #endregion

        #region Properties
        [XmlAttributeAttribute(DataType = "string")]
        public string HostName
        {
            get { return hostName; }
            set { hostName = value; }
        }
        [XmlAttributeAttribute(DataType = "int")]
        public int PortNumber
        {
            get { return portNumber; }
            set { portNumber = value; }
        }

        [XmlAttributeAttribute(DataType = "string")]
        public string UserName
        {
            get { return userName; }
            set { userName = value; }
        }

        /// <summary>
        ///     return or set password which will be saved in configuration files
        /// Ensure that onGet the password is encrypted from internal password 
        /// variable, and onSet the password is decrypted and stored in 
        /// internal variable.
        /// </summary>
        [XmlAttributeAttribute(DataType = "string")]
        public string dbPassword
        {
            get
            {
                return Cryptography.CryptRSA(password); 
            }

            set
            {
                password = Cryptography.DecryptRSA(value); 
            }
        }

        /// <summary>
        ///     Do not allow storing of un-encryped password. Used to bild 
        /// connection strings.
        /// </summary>
        [XmlIgnore()]
        public string Password
        {
            get { return password; }
            set { password = value; }
        }

        [XmlAttributeAttribute(DataType = "string")]
        public string DatabaseName
        {
            get { return databaseName; }
            set { databaseName = value; }
        }
        #endregion


        #region C'tors
        /// <summary>
        /// Copy constructor
        /// </summary>
        /// <param name="other">source object</param>
        public SQLConnectionString (SQLConnectionString other)
        {
            if (other == null) return;

		    hostName = other.HostName;
            portNumber = other.PortNumber;
		    userName = other.UserName;
		    password = other.Password;
		    databaseName = other.DatabaseName;
        }

        /// <summary>
        /// Initialize members from connection string
        /// </summary>
        /// <param name="connectionString">connection string to parce</param>
        public SQLConnectionString (string connectionString)
        {
            try
            {
                ParseConnectionString(connectionString);
            }catch (SystemException) {
            
                hostName = "";
                portNumber = Constants.DataBase.defaultSQLPort;
                userName = "";
                password = "";
                databaseName = "";
            }
        }

        /// <summary>
        ///     Default constructor.
        /// </summary>
        public SQLConnectionString() : base ()
        {
        }

        /// <summary>
        ///     Default constructor which is invoked with indicator that object\
        /// will load it's data from configuration file.
        /// </summary>
        public SQLConnectionString(bool loadFromFile)
            : base()
        {
            if (loadFromFile == false)
            {
                return;
            }
            try
            {
                Object obj = LoadFromFile();
                if (obj != null)
                {
                    hostName = ((SQLConnectionString)obj).HostName;
                    portNumber = ((SQLConnectionString)obj).PortNumber;
                    userName = ((SQLConnectionString)obj).UserName;
                    password = ((SQLConnectionString)obj).Password;
                    databaseName = ((SQLConnectionString)obj).DatabaseName;
                }
            }
            catch (SolDevException)
            {
                // if configuration couldn't be read, use default settings.
            }
        }

        /// <summary>
        ///     fully constructs and initializes connection string
        /// </summary>
        /// <param name="host">host address of the database</param>
        /// <param name="database">database name</param>
        /// <param name="userId">database user id</param>
        /// <param name="password">user's password</param>
        /// <param name="port">port value</param>
        public SQLConnectionString(string hostName, string databaseName, 
                string userName, string password, int portNumber)
        {
            this.hostName = hostName;
            this.databaseName = databaseName;
            this.userName = userName;
            this.password = password;
            this.portNumber = portNumber;
        }

        /// <summary>
        ///     fully constructs and initializes connection string
        /// </summary>
        /// <param name="host">host address of the database</param>
        /// <param name="database">database name</param>
        /// <param name="userId">database user id</param>
        /// <param name="password">user's password</param>
        /// <param name="port">port value</param>
        public SQLConnectionString(string hostName, string databaseName,
                string userName, string password, string portNumber)
        {
            this.hostName = hostName;
            this.databaseName = databaseName;
            this.userName = userName;
            this.password = password;
            this.portNumber = Convert.ToInt32(portNumber);
        }

        /// <summary>
        ///     fully constructs and initializes connection string. Uses default port.
        /// </summary>
        /// <param name="host">host address of the database</param>
        /// <param name="database">database name</param>
        /// <param name="userId">database user id</param>
        /// <param name="password">user's password</param>
        /// <param name="port">port value</param>
        public SQLConnectionString(string hostName, string databaseName,
                string userName, string password)
        {
            this.hostName = hostName;
            this.databaseName = databaseName;
            this.userName = userName;
            this.password = password;
        }
        #endregion
        #region Connection string builders
        /// <summary>
        ///     build standard connection string. Instance free member.
        /// </summary>
        /// <param name="host">host address of the database</param>
        /// <param name="database">database name</param>
        /// <param name="userId">database user id</param>
        /// <param name="password">user's password</param>
        /// <param name="port">port value</param>
        /// <returns>connections string </returns>
        public static string BuildConnectionString(string host, string database, string userId, string password, string port)
        {
            string connectionString = "";

            if (!host.Trim().Equals(string.Empty))
                connectionString = connectionString + "server=" + host.Trim() + "; ";

            if (!database.Trim().Equals(string.Empty))
                connectionString = connectionString + "database=" + database.Trim() + "; ";

            if (!userId.Trim().Equals(string.Empty))
                connectionString = connectionString + "user id=" + userId.Trim() + "; ";

            if (!password.Trim().Equals(string.Empty))
                connectionString = connectionString + "password=" + password.Trim() + "; ";

            if (!port.Trim().Equals(string.Empty))
                connectionString = connectionString + "port=" + port.Trim() + "; ";
            else
                connectionString = connectionString + "port=" + Constants.DataBase.defaultSQLPort.ToString() + "; ";
            return connectionString;
        }

        /// <summary>
        ///     build standard connection string. Instance free member.
        /// </summary>
        /// <param name="host">host address of the database</param>
        /// <param name="database">database name</param>
        /// <param name="userId">database user id</param>
        /// <param name="password">user's password</param>
        /// <param name="port">port value</param>
        /// <returns>connection string</returns>
        public static string BuildConnectionString(string host, string database, string userId, string password, int port)
        {
            string connectionString = "";

            if (!host.Trim().Equals(string.Empty))
                connectionString = connectionString + "server=" + host.Trim() + ";";

            if (!userId.Trim().Equals(string.Empty))
                connectionString = connectionString + "user id=" + userId.Trim() + ";";

            if (!password.Trim().Equals(string.Empty))
                connectionString = connectionString + "password=" + password.Trim() + ";";
            else
                connectionString = connectionString + "password=;";

            if (!database.Trim().Equals(string.Empty))
                connectionString = connectionString + "database=" + database.Trim() + ";";


            connectionString = connectionString + "port=" + port.ToString() + ";";
            connectionString = connectionString + "pooling=false;"; // disable pooling

            return connectionString;
        }

        /// <summary>
        ///     build connection string using instance variables
        /// </summary>
        /// <returns>connection string</returns>
        public string BuildConnectionString()
        {
            return BuildConnectionString(hostName,
                    databaseName, userName, password, portNumber);
        }
        #endregion

        #region Connection string parcers
        /// <summary>
        /// Decompose connection string in available components
        /// </summary>
        /// <throws>
        ///     can throw conversion exception (for portNumber)
        /// </throws>
        /// <param name="connectionString"></param>
        public void ParseConnectionString(string connectionString)
        {
            Match matchResult;

            if (connectionString == null) return;

            if ((matchResult = Regex.Match(connectionString, @"(?i)database\s*=\s*(?<value>[^;]+)")) != null)
                databaseName = matchResult.Result("${value}");
            else
                databaseName = "";

            if ((matchResult = Regex.Match(connectionString, @"(?i)server\s*=\s*(?<value>[^;]+)")) != null)
                hostName = matchResult.Result("${value}");
            else
                hostName = "";

            if ((matchResult = Regex.Match(connectionString, @"(?i)user id\s*=\s*(?<value>[^;]+)")) != null)
                userName = matchResult.Result("${value}");
            else
                userName = "";

            if ((matchResult = Regex.Match(connectionString, @"(?i)password\s*=\s*(?<value>[^;]+)")) != null)
                password = matchResult.Result("${value}");
            else
                password = "";

            if ((matchResult = Regex.Match(connectionString, @"(?i)port\s*=\s*(?<value>[^;]+)")) != null)
                portNumber = Convert.ToInt32(matchResult.Result("${value}"));
            else
                portNumber = Constants.DataBase.defaultSQLPort;

        }
        #endregion

        #region Copy routines
        /// <summary>
        /// Copy from passed object. 
        /// </summary>
        /// <param name="other">source object</param>
        /// <returns>Return pointer to modified object (itself)</returns>
        public SQLConnectionString copyFrom(SQLConnectionString other)
        {
            if (other == null) return null;

            hostName = other.HostName;
            portNumber = other.PortNumber;
            userName = other.UserName;
            password = other.Password;
            databaseName = other.DatabaseName;
            return this;
        }

        /// <summary>
        /// Copy from passed object
        /// </summary>
        /// <param name="other">destination object</param>
        /// <returns>Return pointer to modified object (other object)</returns>
        public SQLConnectionString copyTo(SQLConnectionString other)
        {
            if (other == null) return null;

            other.HostName = hostName;
            other.PortNumber = portNumber;
            other.UserName = userName;
            other.Password = password;
            other.DatabaseName = databaseName;
            return other;
        }

        #endregion
    }
}
